import { useState } from 'react';
import React from 'react';
import {
  gql,
  useMutation,
  useLazyQuery,
  useSubscription,
} from '@apollo/client';
import {
  Card,
  Typography,
  Button,
  Table,
  Layout,
  Modal,
  Form,
  Input,
  Space,
  Alert,
  Descriptions,
  Upload,
  Image,
} from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import { useClient } from './GraphQLProvider';

const { Title } = Typography;

const GET_OWNED_NFTS = gql`
  query OwnedNfts($owner: AccountOwner!) {
    ownedNfts(owner: $owner)
  }
`;

const MINT_NFT = gql`
  mutation Mint(
    $minter: AccountOwner!
    $name: String!
    $blobHash: CryptoHash!
  ) {
    mint(minter: $minter, name: $name, blobHash: $blobHash)
  }
`;

const PUBLISH_DATA_BLOB = gql`
  mutation PublishDataBlob($chainId: ChainId!, $bytes: [Int!]!) {
    publishDataBlob(chainId: $chainId, bytes: $bytes)
  }
`;

const TRANSFER_NFT = gql`
  mutation Transfer(
    $sourceOwner: AccountOwner!
    $tokenId: String!
    $targetAccount: Account!
  ) {
    transfer(
      sourceOwner: $sourceOwner
      tokenId: $tokenId
      targetAccount: $targetAccount
    )
  }
`;

const NOTIFICATION_SUBSCRIPTION = gql`
  subscription Notifications($chainId: ID!) {
    notifications(chainId: $chainId)
  }
`;

const getFileBase64 = (file) =>
  new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () => resolve(reader.result);
    reader.onerror = (error) => reject(error);
  });

const normFile = (e) => {
  return e?.fileList[0];
};

function App({ chainId, owner }) {
  const { appClient, nodeServiceClient } = useClient();
  // Error
  const [transferError, setTransferError] = useState('');
  const [mintError, setMintError] = useState('');

  // Dialog control
  const [isMintOpen, setMintOpen] = useState(false);
  const [isTransferOpen, setTransferOpen] = useState(false);

  // Transfer dialog
  const [tokenID, setTokenID] = useState('');
  const [targetChainID, setTargetChainID] = useState('');
  const [targetOwner, setTargetOwner] = useState('');
  const [transferForm] = Form.useForm();

  // Mint dialog
  const [name, setName] = useState('');
  const [mintForm] = Form.useForm();
  const [mintPreviewOpen, setMintPreviewOpen] = useState(false);
  const [mintPreviewImage, setMintPreviewImage] = useState('');
  const [mintPreviewTitle, setMintPreviewTitle] = useState('');
  const [mintUploadedFileList, setMintUploadedFileList] = useState([]);
  const [mintImageUrl, setMintImageUrl] = useState('');

  let [
    getOwnedNfts,
    { data: ownedNftsData, called: ownedNftsCalled, loading: ownedNftsLoading },
  ] = useLazyQuery(GET_OWNED_NFTS, {
    client: appClient,
    fetchPolicy: 'network-only',
    variables: { owner: `${owner}` },
  });

  const [transferNft, { loading: transferLoading }] = useMutation(
    TRANSFER_NFT,
    {
      client: appClient,
      onError: (error) => setTransferError('Transfer Error: ' + error.message),
      onCompleted: () => {
        handleTransferClose();
        getOwnedNfts(); // Refresh owned NFTs list
      },
    }
  );

  const [publishDataBlob, { loading: publishDataBlobLoading }] = useMutation(
    PUBLISH_DATA_BLOB,
    {
      client: nodeServiceClient,
      onError: (error) =>
        setMintError('Publish Data Blob Error: ' + error.message),
      onCompleted: () => {},
    }
  );

  const [mintNft, { loading: mintLoading }] = useMutation(MINT_NFT, {
    client: appClient,
    onError: (error) => setMintError('Mint Error: ' + error.message),
    onCompleted: () => {
      handleMintClose();
      getOwnedNfts(); // Refresh owned NFTs list
    },
  });

  if (!ownedNftsCalled) {
    void getOwnedNfts();
  }

  useSubscription(NOTIFICATION_SUBSCRIPTION, {
    client: appClient,
    variables: { chainId: chainId },
    onData: () => getOwnedNfts(), // Refresh on new notifications
  });

  const handleMintOpen = () => setMintOpen(true);
  const handleMintClose = () => {
    setMintOpen(false);
    resetMintDialog();
  };

  const handleTransferOpen = (token_id) => {
    setTokenID(token_id);
    setTransferOpen(true);
  };
  const handleTransferClose = () => {
    setTransferOpen(false);
    resetTransferDialog();
  };

  const resetMintDialog = () => {
    setName('');
    setMintError('');
    setMintUploadedFileList([]);
    setMintImageUrl('');
    mintForm.resetFields();
  };

  // Placeholder for form submission logic
  const handleMintSubmit = () => {
    const encoder = new TextEncoder();
    const byteArrayFile = encoder.encode(mintImageUrl);

    publishDataBlob({
      variables: {
        chainId: chainId,
        bytes: Array.from(byteArrayFile),
      },
    }).then((r) => {
      if ('errors' in r) {
        console.log(
          'Got error while publishing Data Blob: ' + JSON.stringify(r, null, 2)
        );
      } else {
        console.log('Data Blob published: ' + JSON.stringify(r, null, 2));
        const blobHash = r['data']['publishDataBlob'];
        mintNft({
          variables: {
            minter: `${owner}`,
            name: name,
            blobHash: blobHash,
          },
        }).then((r) => {
          if ('errors' in r) {
            console.log(
              'Got error while minting NFT: ' + JSON.stringify(r, null, 2)
            );
          } else {
            console.log('NFT minted: ' + JSON.stringify(r, null, 2));
          }
        });
      }
    });
  };

  const resetTransferDialog = () => {
    setTokenID('');
    setTargetChainID('');
    setTargetOwner('');
    setTransferError('');
    transferForm.resetFields();
  };

  const handleTransferSubmit = () => {
    transferNft({
      variables: {
        sourceOwner: `${owner}`,
        tokenId: tokenID,
        targetAccount: {
          chainId: targetChainID,
          owner: `${targetOwner}`,
        },
      },
    }).then((r) => {
      if ('errors' in r) {
        console.log(
          'Error while transferring NFT: ' + JSON.stringify(r, null, 2)
        );
      } else {
        console.log('NFT transferred: ' + JSON.stringify(r, null, 2));
      }
    });
  };

  const onTransferValuesChange = (values) => {
    if (values.target_chain_id !== undefined) {
      setTargetChainID(values.target_chain_id);
    }

    if (values.target_owner !== undefined) {
      setTargetOwner(values.target_owner);
    }
  };

  const onMintValuesChange = (values) => {
    if (values.name !== undefined) {
      setName(values.name);
    }
  };

  const handleMintPreviewCancel = () => setMintPreviewOpen(false);
  const handleMintPreview = (file) => {
    setMintPreviewImage(file.url || file.preview);
    setMintPreviewOpen(true);
    setMintPreviewTitle(
      file.name || file.url.substring(file.url.lastIndexOf('/') + 1)
    );
  };

  const handleUploadChange = async ({ fileList: newFileList }) => {
    if (newFileList.length > 0) {
      const imageDataUrl = await getFileBase64(newFileList[0].originFileObj);
      newFileList[0].preview = imageDataUrl;
      if (newFileList[0] !== undefined) {
        delete newFileList[0].error;
        newFileList[0].status = 'done';
      }
      setMintImageUrl(imageDataUrl);
    } else {
      setMintImageUrl('');
    }
    setMintUploadedFileList(newFileList);
  };

  const uploadButton = (
    <button
      style={{
        border: 0,
        background: 'none',
      }}
      type='button'
    >
      <PlusOutlined />
      <div
        style={{
          marginTop: 8,
        }}
      >
        Upload
      </div>
    </button>
  );

  const columns = [
    {
      title: 'Token Id',
      dataIndex: 'token_id',
      key: 'token_id',
    },
    {
      title: 'Name',
      dataIndex: 'name',
      key: 'name',
    },
    {
      title: 'Minter',
      dataIndex: 'minter',
      key: 'minter',
    },
    {
      title: 'Image',
      dataIndex: 'image',
      key: 'image',
      render: (_, nft) => (
        <Image
          width={80}
          height={80}
          src={nft.payload}
          fallback=''
        />
      ),
    },
    {
      title: 'Transfer',
      dataIndex: 'transfer',
      key: 'transfer',
      render: (_, nft) => (
        <Button onClick={() => handleTransferOpen(nft.token_id)}>
          Transfer
        </Button>
      ),
    },
  ];

  const userInfoItems = [
    {
      key: 'account',
      label: 'Account',
      children: owner,
    },
    {
      key: 'chain',
      label: 'Chain',
      children: chainId,
    },
  ];

  return (
    <Layout sx={{ mt: 4, overflowX: 'auto' }}>
      <Card sx={{ minWidth: 'auto', width: '100%', mx: 'auto', my: 2 }}>
        <Title>Linera NFT</Title>
        <Space
          direction='vertical'
          style={{
            display: 'flex',
          }}
        >
          <Descriptions title='User Info' items={userInfoItems} column={1} />

          <Typography style={{ fontWeight: 'bold' }} variant='h6'>
            Your Owned NFTs:
          </Typography>
          <Table
            columns={columns}
            loading={ownedNftsLoading}
            dataSource={
              ownedNftsData
                ? Object.entries(ownedNftsData.ownedNfts).map(
                  ([token_id, nft]) => {
                    const decoder = new TextDecoder();
                    const deserializedImage = decoder.decode(
                      new Uint8Array(nft.payload)
                    );

                    return {
                      key: token_id,
                      token_id: token_id,
                      name: nft.name,
                      minter: nft.minter,
                      payload: deserializedImage,
                    };
                  }
                )
                : []
            }
          />

          <Button type='primary' onClick={handleMintOpen}>
            Mint
          </Button>
        </Space>

        <Modal
          title='Mint NFT'
          open={isMintOpen}
          footer={null}
          onCancel={handleMintClose}
        >
          <Form
            name='mint'
            labelCol={{
              span: 8,
            }}
            wrapperCol={{
              span: 16,
            }}
            style={{
              maxWidth: 600,
            }}
            form={mintForm}
            autoComplete='off'
            onValuesChange={onMintValuesChange}
            disabled={mintLoading || publishDataBlobLoading}
          >
            <Space
              direction='vertical'
              style={{
                display: 'flex',
              }}
            >
              {mintError ? (
                <Alert
                  message='Error'
                  description={mintError}
                  type='error'
                  showIcon
                />
              ) : null}
              <Form.Item
                label='Name'
                name='name'
                rules={[
                  {
                    required: true,
                    message: 'Please input the name!',
                  },
                ]}
              >
                <Input />
              </Form.Item>
            </Space>

            <Form.Item label='Image'>
              <Form.Item
                rules={[
                  {
                    required: true,
                    message: 'Please input the image!',
                  },
                ]}
                name='image'
                valuePropName='imageList'
                getValueFromEvent={normFile}
                noStyle
              >
                <Upload
                  listType='picture-card'
                  fileList={mintUploadedFileList}
                  onPreview={handleMintPreview}
                  onChange={handleUploadChange}
                  accept='image/*'
                >
                  {mintUploadedFileList.length >= 1 ? null : uploadButton}
                </Upload>
              </Form.Item>
            </Form.Item>

            <Form.Item
              wrapperCol={{
                offset: 8,
                span: 16,
              }}
            >
              <Space>
                <Button
                  type='primary'
                  onClick={handleMintSubmit}
                  loading={mintLoading || publishDataBlobLoading}
                >
                  Submit
                </Button>
                <Button onClick={handleMintClose}>Cancel</Button>
              </Space>
            </Form.Item>
          </Form>
        </Modal>

        <Modal
          open={mintPreviewOpen}
          title={mintPreviewTitle}
          footer={null}
          onCancel={handleMintPreviewCancel}
        >
          <img
            alt=''
            style={{
              width: '100%',
            }}
            src={mintPreviewImage}
          />
        </Modal>

        <Modal
          title='Transfer NFT'
          open={isTransferOpen}
          footer={null}
          onCancel={handleTransferClose}
        >
          <Form
            name='transfer'
            labelCol={{
              span: 8,
            }}
            wrapperCol={{
              span: 16,
            }}
            style={{
              maxWidth: 600,
            }}
            initialValues={{
              token_id: tokenID,
            }}
            form={transferForm}
            autoComplete='off'
            onValuesChange={onTransferValuesChange}
            disabled={transferLoading}
          >
            {transferError ? (
              <Alert
                message='Error'
                description={transferError}
                type='error'
                showIcon
              />
            ) : null}
            <Form.Item
              label='Token Id'
              name='token_id'
              rules={[
                {
                  required: true,
                  message: 'Please input the token id!',
                },
              ]}
            >
              <Input disabled />
            </Form.Item>

            <Form.Item
              label='Target Chain Id'
              name='target_chain_id'
              rules={[
                {
                  required: true,
                  message: 'Please input the target chain ID!',
                },
              ]}
            >
              <Input />
            </Form.Item>

            <Form.Item
              label='Target Owner'
              name='target_owner'
              rules={[
                {
                  required: true,
                  message: 'Please input the target owner!',
                },
              ]}
            >
              <Input />
            </Form.Item>

            <Form.Item
              wrapperCol={{
                offset: 8,
                span: 16,
              }}
            >
              <Space>
                <Button
                  type='primary'
                  onClick={handleTransferSubmit}
                  loading={transferLoading}
                >
                  Submit
                </Button>
                <Button onClick={handleTransferClose}>Cancel</Button>
              </Space>
            </Form.Item>
          </Form>
        </Modal>
      </Card>
    </Layout>
  );
}

export default App;
